Skip to content

Conversation

@HoustonPutman
Copy link
Contributor

https://issues.apache.org/jira/browse/SOLR-18072

There are no functionality changes here. Just test improvements, and refactoring of the APIs.

I'm also happy to call AdminCmdContext something else, that's a very easy change.

Copy link
Contributor

@dsmiley dsmiley left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I overall like the proposal of an Admin API context class for these APIs.

I've been looking in this PR for where SubResponseAccumulatingJerseyResponse is defined but can't find it; maybe because Safari/GitHub is completely choking on this massive PR. I don't love the name of that thing. Can you please self-review to identify it?

CollectionAdminRequest.RequestStatus requestStatus =
CollectionAdminRequest.requestStatus(asyncId);
CollectionAdminRequest.RequestStatusResponse rsp = null;
TimeOut timeoutCheck = new TimeOut(timeout, TimeSource.NANO_TIME);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm; we also have org.apache.solr.common.util.RetryUtil utility. Maybe that should at least cross-link.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can switch over to that.

import org.mockito.ArgumentCaptor;

/**
* Abstract test class to setup shared mocks for unit testing v2 API calls that go to the Overseer.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

false

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overseer or DistributedCollectionConfigSetCommandRunner, fixed.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Based on the content here, this seems actually for SolrCloud. If so; maybe the name or package should reflect that. Any way, this class is peculiar to me.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could move it, but all of these test classes already lived here, and this is just an abstract (will change) class to help with the mocking for most of the other test classes in this package.

@HoustonPutman
Copy link
Contributor Author

I've been looking in this PR for where SubResponseAccumulatingJerseyResponse is defined but can't find it; maybe because Safari/GitHub is completely choking on this massive PR. I don't love the name of that thing. Can you please self-review to identify it?

This response class already exists, and the method that populated it already existed too. I just expanded the use of it, so that we weren't doing the same thing across each API independently. That's why you can't find it in the PR. We can change the name independently if you dislike it, but not really in the scope of this PR.

Copy link
Contributor

@dsmiley dsmiley left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks.

}
fail("Async request " + asyncId + " did not complete within duration: " + timeout.toString());
return rsp;
final AtomicReference<CollectionAdminRequest.RequestStatusResponse> rsp =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks worse. I guess TimeOut is simpler in part due to no try-catch needed in some cases (like this) and no AtomicReference needed either. Dodging the try-catch need is unusual... normally a failing request propagates an exception but I see the API you are using doesn't do that. I think that's a known GAP in our APIs that I had been chatting with Jason about solving.

Any way, do as you please.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, agreed.

Ok at first I was going to just revert the change. But then I decided adding an additional retry utility that would return a response, that is validated, would be better. The method looks MUCH better now.

Copy link
Contributor

@gerlowskija gerlowskija left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Publishing a partial review to make a few in-line comments visible. Hoping to have the second half of my review available shortly...

summary = "Deletes an alias by its name",
tags = {"aliases"})
SolrJerseyResponse deleteAlias(
SubResponseAccumulatingJerseyResponse deleteAlias(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Q] Why are we narrowing the response schema of this API?

Typically "SubResponseAccumulatingJerseyResponse" gets used for those admin APIs where the overseer sends out requests to individual replicas, etc. and includes each of those sub-responses in what gets returned to the original caller. But looking at DeleteAliasCmd, it doesn't seem to be doing anything like that?

There's a (minor?) downside to doing this in that our OpenAPI spec (and everything that gets generated from it) will now look like the additional fields will be present in the API response.

(Note: this question applies to a few of the files above as well, but just leaving it in this one place to avoid duplication.)

collectionParams,
ccc.getCoreContainer().getConfigSetService());

Map<String, Object> propMap = new HashMap<>();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[0] I've seen a few little snippets like this one so far; might be worth some kind of "cloneZkPropsWithOperation" utility method 🤷

String internalAsyncId = asyncId + Math.abs(System.nanoTime());
props.put(ASYNC, internalAsyncId);
String internalAsyncId = null;
if (adminCmdContext.getAsyncId() != null) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Q] Wdyt of having some sort of getInternalAsyncId() or getSubrequestAsyncId() method in AdminCmdContext that would encapsulate this logic?

Seems like it's something a number of cmd implementations have to figure out for themselves, and would definitely fit under the general umbrella of "admin command context" conceptually.

Copy link
Contributor

@gerlowskija gerlowskija left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great overall!

I left a few minor comments here and there - I think the most significant of which are around some of the new SubResponseAccumulatingJerseyResponse usages. But this looks great, even with those quibbles.

@Override
@PermissionName(COLL_EDIT_PERM)
public SolrJerseyResponse addReplicaProperty(
public SubResponseAccumulatingJerseyResponse addReplicaProperty(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[+1] Good catch

@PermissionName(COLL_EDIT_PERM)
public SolrJerseyResponse deleteAliasProperty(String aliasName, String propName)
throws Exception {
public SubResponseAccumulatingJerseyResponse deleteAliasProperty(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[-0] All of the "update" methods in this class shouldn't return SubResponseAccumulatingJerseyResponse, afaict, because the API response never uses the "successes" or "failures" fields that are the main reason for SubResponse... to exist.

If we aim of this change is to support async alias-prop modification, we can probably use AsyncJerseyResponse instead?

public SolrJerseyResponse createAlias(CreateAliasRequestBody requestBody) throws Exception {
final SubResponseAccumulatingJerseyResponse response =
instantiateJerseyResponse(SubResponseAccumulatingJerseyResponse.class);
public SubResponseAccumulatingJerseyResponse createAlias(CreateAliasRequestBody requestBody)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[-0] Idk why L88 was instantiating a "SubResponseAccumulatingJerseyResponse" prior to this PR, maybe a copy-paste error on my part?

But in reality, this API never sets the "successes" or "failures" fields that are the main reason for SubResponse... to exist. So rather than switching the return type here to SubResponse, we should probably make it "SolrJerseyResponse" in both places? Or maybe "AsyncJerseyResponse" if we want to support asynchronous alias creation?

throw remoteResponse.getException();
}

if (requestBody.async != null) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[+1] Such a huge improvement that each individual API doesn't need to check for or copy over these fields anymore!

response.requestId = asyncId;
}

public SubResponseAccumulatingJerseyResponse deleteAlias(String aliasName, String asyncId)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[-0] Another place, afaict, where the original "SolrJerseyResponse" better reflects the actual response content that we expect.

ArrayList<String> l = new ArrayList<>(liveNodes);
Collections.shuffle(l, random());
CollectionAdminRequest.Create create =
pickRandom(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[+1] This is a cool little pattern 👍

* Abstract test class to setup shared mocks for unit testing v2 API calls that go to the Overseer
* or the DistributedCollectionConfigSetCommandRunner.
*/
public abstract class MockAPITest extends SolrTestCaseJ4 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Q] Should the test name have something in it to indicate it's "v2"-ness, and that it's not just for any old API you want to mock?

assertEquals("someRepository", remoteMessage.get(BACKUP_REPOSITORY));
assertEquals("someBackupName", remoteMessage.get(NAME));

AdminCmdContext context = contextCapturer.getValue();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[0] Most (all?) of these mock-based v2 API tests have some version of this little snippet where we're grabbing the AdminCmdContext out of a capturer and asserting the action and async ID. Just flagging it as there might be a way to pull it into a helper method in MockAPITest.

}

/** Returns whether the request status should be refreshed given the current state. */
public boolean shouldRefresh() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[0] Idk if "refresh" means anything to most users. Maybe isOperationFinished or something similar? Or maybe ignore me and "refresh" is the best option as anything else would risk causing confusion with the "COMPLETED" enum value.

Ignore me, just thinking aloud I guess...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree on "refresh" not being the most clear word. Maybe isFinal or isFinished, which implies you may want to get the state again.

}
}

protected CollectionAdminRequest.RequestStatusResponse waitForAsyncClusterRequest(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[0] Is there anything SolrCloudTestCase-specific about this utility? Not all of our SolrCloud tests use SolrCloudTestCase, so it might be nice to put it in some place where it can be used by any SolrCloud-relevant test 🤷

}

/** Returns whether the request status should be refreshed given the current state. */
public boolean shouldRefresh() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree on "refresh" not being the most clear word. Maybe isFinal or isFinished, which implies you may want to get the state again.

* an exception
* @throws SolrException if all retries are exhausted without success
*/
public static <T> T retryUntil(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants